#include "esp_err.h"
#include "s3.h"

static const char *TAG = "S3.c";

/*GPIO学习，函数调用test()*/
static QueueHandle_t gpio_queue = NULL;

static void gpio_isr_handle(void *arg)
{
	static uint32_t data;
	xQueueSendFromISR(gpio_queue, &data, NULL);
	data++;
}

static void gpio_task(void *arg)
{
	uint32_t data;
	while (1)
	{
		xQueueReceive(gpio_queue, &data, portMAX_DELAY);
		printf("gpio_isr_handle, data:%ld\n", data);
		vTaskDelay(1000 / portTICK_PERIOD_MS);
	}
}

esp_err_t test(void)
{
	esp_err_t ret = ESP_OK;
	gpio_config_t io_conf;
	io_conf.intr_type = GPIO_INTR_NEGEDGE;
	io_conf.mode = GPIO_MODE_INPUT;
	io_conf.pin_bit_mask = (1ULL << SW0_PIN);
	io_conf.pull_down_en = 0;
	io_conf.pull_up_en = 0;
	gpio_config(&io_conf);

	/*创建一个队列，包含10个uint32_t的值*/
	gpio_queue = xQueueCreate(10, sizeof(uint32_t));

	xTaskCreate(gpio_task, "gpio_tack", 2048, NULL, 5, NULL);

	gpio_install_isr_service(0);

	gpio_isr_handler_add(SW0_PIN, gpio_isr_handle, NULL);
	return ret;
}

// i2c_master_bus_handle_t bus_handle;
// i2c_master_dev_handle_t i2c_dev_pca9557;
esp_err_t i2c_init(void)
{
	ESP_LOGI(TAG, "i2c_init");
	esp_err_t ret = ESP_OK;

	// i2c_master_bus_config_t i2c_mst_config = {
	// 	.clk_source = I2C_CLK_SRC_DEFAULT,
	// 	.i2c_port = -1,
	// 	.scl_io_num = I2C_SCL_PIN,
	// 	.sda_io_num = I2C_SDA_PIN,
	// 	.glitch_ignore_cnt = 7,
	// };
	// ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_mst_config, &bus_handle));
	i2c_config_t i2c_conf = {
		.mode = I2C_MODE_MASTER,
		.sda_io_num = I2C_SDA_PIN,
		.sda_pullup_en = GPIO_PULLUP_ENABLE,
		.scl_io_num = I2C_SCL_PIN,
		.scl_pullup_en = GPIO_PULLUP_ENABLE,
		.master.clk_speed = I2C_FREQ_HZ,
	};
	i2c_param_config(S3_I2C_NUM, &i2c_conf);
	i2c_driver_install(S3_I2C_NUM, i2c_conf.mode, 0, 0, 0);

	return ret;
}

/*IO扩展*/
void PCA9557_Init(void)
{
	// i2c_device_config_t dev_cfg = {
	// 	.dev_addr_length = I2C_ADDR_BIT_LEN_7,
	// 	.device_address = PCA9557_ADDR,
	// 	.scl_speed_hz = 100000,
	// };
	// ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_cfg, &i2c_dev_pca9557));

	uint8_t dat[2];
	dat[0] = PCA9557_CONFIGURATION_PORT;
	dat[1] = 0xf8; // 1111 1000,IO0-3输出，IO4-7输入
	// i2c_master_transmit(i2c_dev_pca9557, dat, 2, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_to_device(S3_I2C_NUM, PCA9557_ADDR, dat, 2, 1000 / portTICK_PERIOD_MS); // legacy i2c

	dat[0] = PCA9557_OUTPUT_PORT;
	dat[1] = 0x05;																			 // 0000 0101
																							 // i2c_master_transmit(i2c_dev_pca9557, dat, 2, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_to_device(S3_I2C_NUM, PCA9557_ADDR, dat, 2, 1000 / portTICK_PERIOD_MS); // legacy i2c
}

void lcd_cs(uint8_t level)
{
	uint8_t dat[2], r_dat;
	dat[0] = PCA9557_INPUT_PORT;
	// i2c_master_transmit_receive(i2c_dev_pca9557, dat, 1, &r_dat, 1, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_read_device(S3_I2C_NUM, PCA9557_ADDR, dat, 1, &r_dat, 1, 1000 / portTICK_PERIOD_MS); // legacy i2c
	dat[0] = PCA9557_OUTPUT_PORT;
	if (level)
		dat[1] = r_dat | LCD_CS_GPIO;
	else
		dat[1] = r_dat & (~LCD_CS_GPIO);
	// i2c_master_transmit(i2c_dev_pca9557, dat, 2, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_to_device(S3_I2C_NUM, PCA9557_ADDR, dat, 2, 1000 / portTICK_PERIOD_MS); // legacy i2c
}

void camera_pwdn(uint8_t level)
{
	uint8_t dat[2], r_dat;
	dat[0] = PCA9557_INPUT_PORT;
	// i2c_master_transmit_receive(i2c_dev_pca9557, dat, 1, &r_dat, 1, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_read_device(S3_I2C_NUM, PCA9557_ADDR, dat, 1, &r_dat, 1, 1000 / portTICK_PERIOD_MS); // legacy i2c
	dat[0] = PCA9557_OUTPUT_PORT;
	if (level)
		dat[1] = r_dat | DVP_PWDN_GPIO;
	else
		dat[1] = r_dat & (~DVP_PWDN_GPIO);
	// i2c_master_transmit(i2c_dev_pca9557, dat, 2, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_to_device(S3_I2C_NUM, PCA9557_ADDR, dat, 2, 1000 / portTICK_PERIOD_MS); // legacy i2c
}

void pa_en(uint8_t level)
{
	uint8_t dat[2], r_dat;
	dat[0] = PCA9557_INPUT_PORT;
	// i2c_master_transmit_receive(i2c_dev_pca9557, dat, 1, &r_dat, 1, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_read_device(S3_I2C_NUM, PCA9557_ADDR, dat, 1, &r_dat, 1, 1000 / portTICK_PERIOD_MS); // legacy i2c
	dat[0] = PCA9557_OUTPUT_PORT;
	if (level)
		dat[1] = r_dat | PA_EN_GPIO;
	else
		dat[1] = r_dat & (~PA_EN_GPIO);
	// i2c_master_transmit(i2c_dev_pca9557, dat, 2, 1000 / portTICK_PERIOD_MS);
	i2c_master_write_to_device(S3_I2C_NUM, PCA9557_ADDR, dat, 2, 1000 / portTICK_PERIOD_MS); // legacy i2c
}

/*LCD*/
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t lcd_panel_handle;

static esp_err_t ledc_init(void)
{
	ledc_timer_config_t ledc_timer = {
		.timer_num = LEDC_TIMER_0,
		.speed_mode = LEDC_LOW_SPEED_MODE,
		.duty_resolution = LEDC_TIMER_10_BIT,
		.freq_hz = 4000,
		.clk_cfg = LEDC_AUTO_CLK,
	};
	ledc_timer_config(&ledc_timer);
	ledc_channel_config_t ledc_channel_cfg = {
		.channel = LEDC_CHANNEL_0,
		.duty = 0,
		.gpio_num = LCD_BL_PIN,
		.flags.output_invert = 1,
		.speed_mode = LEDC_LOW_SPEED_MODE,
		.hpoint = 0,
		.timer_sel = LEDC_TIMER_0,
		.intr_type = LEDC_INTR_DISABLE,
	};
	ledc_channel_config(&ledc_channel_cfg);
	return ESP_OK;
}
esp_err_t lcd_init(void)
{
	ledc_init();
	spi_bus_config_t buscfg = {
		.mosi_io_num = LCD_MOSI_PIN,
		.sclk_io_num = LCD_CLK_PIN,
		.miso_io_num = -1,
		.quadwp_io_num = -1,
		.quadhd_io_num = -1,
		.max_transfer_sz = LCD_V_RES * LCD_H_RES * sizeof(uint16_t),
	};
	spi_bus_initialize(SPI_LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);

	esp_lcd_panel_io_spi_config_t io_config = {
		.dc_gpio_num = LCD_DC_PIN,
		.pclk_hz = 80 * 1000 * 1000,
		.spi_mode = 2,
		.cs_gpio_num = -1,
		.lcd_cmd_bits = 8,
		.lcd_param_bits = 8,
		.trans_queue_depth = 10,
	};
	esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI_LCD_HOST, &io_config, &io_handle);

	esp_lcd_panel_dev_config_t panel_config = {
		.reset_gpio_num = -1,
		.rgb_ele_order = LCD_RGB_ENDIAN_RGB,
		// .data_endian = LCD_RGB_DATA_ENDIAN_LITTLE,
		.bits_per_pixel = 16,
	};
	esp_lcd_new_panel_st7789(io_handle, &panel_config, &lcd_panel_handle);

	esp_lcd_panel_reset(lcd_panel_handle);
	lcd_cs(0);
	esp_lcd_panel_init(lcd_panel_handle);
	esp_lcd_panel_invert_color(lcd_panel_handle, true);
	esp_lcd_panel_disp_on_off(lcd_panel_handle, true);

	esp_lcd_panel_swap_xy(lcd_panel_handle, true);
	esp_lcd_panel_mirror(lcd_panel_handle, true, false);

	ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 1023 * 50 / 100);
	ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
	return ESP_OK;
}

/* touch */
esp_lcd_touch_handle_t tp;
esp_lcd_panel_io_handle_t tp_io_handle;
void lcd_touch_init(void)
{
	ESP_LOGI(TAG, "Initializing touch panel");
	esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
	// io_config.scl_speed_hz = I2C_FREQ_HZ;// legacy i2c no need
	// esp_lcd_new_panel_io_i2c(bus_handle, &io_config, &tp_io_handle); // new i2c
	esp_lcd_new_panel_io_i2c(S3_I2C_NUM, &io_config, &tp_io_handle); // legacy i2c

	esp_lcd_touch_config_t tp_cfg = {
		.x_max = LCD_V_RES,
		.y_max = LCD_H_RES,
		.rst_gpio_num = -1,
		.int_gpio_num = -1,
		.levels = {
			.reset = 0,
			.interrupt = 0,
		},
		.flags = {
			.swap_xy = 1,
			.mirror_x = 1,
			.mirror_y = 0,
		},
	};

	esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp);
}

void lv_start(void)
{
	const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
	lvgl_port_init(&lvgl_cfg);
	const lvgl_port_display_cfg_t disp_cfg = {
		.io_handle = io_handle,
		.panel_handle = lcd_panel_handle,
		.buffer_size = LCD_V_RES * LCD_H_RES,
		.double_buffer = 0,
		.hres = LCD_H_RES,
		.vres = LCD_V_RES,
		.monochrome = false,
		.rotation = {
			.swap_xy = 1,
			.mirror_x = 1,
			.mirror_y = 0,
		},
		.flags = {
			.buff_dma = 0,
			.buff_spiram = 1,
		}};
	lv_disp_t *disp;

	disp = lvgl_port_add_disp(&disp_cfg);

	/* touch */
	lvgl_port_touch_cfg_t touch_cfg = {
		.disp = disp,
		.handle = tp,
	};
	lvgl_port_add_touch(&touch_cfg);
}

esp_err_t camera_init()
{
	camera_pwdn(0);

	camera_config_t camera_config = {
		.pin_pwdn = CAM_PIN_PWDN,
		.pin_reset = CAM_PIN_RESET,
		.pin_xclk = CAM_PIN_XCLK,
		.pin_sccb_sda = -1,
		.pin_sccb_scl = -1,

		.pin_d7 = CAM_PIN_D7,
		.pin_d6 = CAM_PIN_D6,
		.pin_d5 = CAM_PIN_D5,
		.pin_d4 = CAM_PIN_D4,
		.pin_d3 = CAM_PIN_D3,
		.pin_d2 = CAM_PIN_D2,
		.pin_d1 = CAM_PIN_D1,
		.pin_d0 = CAM_PIN_D0,
		.pin_vsync = CAM_PIN_VSYNC,
		.pin_href = CAM_PIN_HREF,
		.pin_pclk = CAM_PIN_PCLK,

		.xclk_freq_hz = XCLK_FREQ_HZ,
		.ledc_timer = LEDC_TIMER_1,
		.ledc_channel = LEDC_CHANNEL_1,

		.pixel_format = PIXFORMAT_RGB565, // YUV422,GRAYSCALE,RGB565,JPEG
		.frame_size = FRAMESIZE_QVGA,	  // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.

		.jpeg_quality = 12,					// 0-63, for OV series camera sensors, lower number means higher quality
		.fb_count = 2,						// WhenFRAMESIZE_UXGA jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
		.grab_mode = CAMERA_GRAB_WHEN_EMPTY // CAMERA_GRAB_LATEST. Sets when buffers should be filled
	}; // initialize the camera

	esp_err_t err = esp_camera_init(&camera_config);
	if (err != ESP_OK)
	{
		ESP_LOGE(TAG, "Camera Init Failed");
		return err;
	}
	// sensor_t *s = esp_camera_sensor_get();
	// if (s->id.PID == GC0308_PID)
	// {
	// 	s->set_hmirror(s, 1); // 这里控制摄像头镜像 写1镜像 写0不镜像
	// }

	return ESP_OK;
}

esp_err_t camera_capture()
{
	// acquire a frame
	camera_fb_t *fb = esp_camera_fb_get();
	if (!fb)
	{
		ESP_LOGE(TAG, "Camera Capture Failed");
		return ESP_FAIL;
	}
	// replace this with your own function
	// process_image(fb->width, fb->height, fb->format, fb->buf, fb->len);
	esp_lcd_panel_draw_bitmap(lcd_panel_handle, 0, 0, fb->width, fb->height, fb->buf);

	// return the frame buffer back to the driver for reuse
	esp_camera_fb_return(fb);
	return ESP_OK;
}

esp_err_t sd_init()
{
	esp_err_t ret;
	sdmmc_card_t *card;
	esp_vfs_fat_sdmmc_mount_config_t mount_config = {
		.format_if_mount_failed = true,
		.max_files = 5,
		.allocation_unit_size = 16 * 1024,
	};
	ESP_LOGI(TAG, "Initializing SD card");
	ESP_LOGI(TAG, "Using SDMMC peripheral");
	sdmmc_host_t host = SDMMC_HOST_DEFAULT();
	sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
	slot_config.width = 1;
	slot_config.clk = SD_CLK_PIN;
	slot_config.cmd = SD_CMD_PIN;
	slot_config.d0 = SD_D0_PIN;
	slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

	ESP_LOGI(TAG, "Mouting filesystem");
	ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
	if (ret != ESP_OK)
	{
		ESP_LOGE(TAG, "Failed to mount filesystem. Error: %s", esp_err_to_name(ret));
		return ret;
	}
	ESP_LOGI(TAG, "Filesystem mounted successfully");
	sdmmc_card_print_info(stdout, card);
	return ret;
}

esp_err_t es8311_codec_init()
{
	es8311_handle_t es_handle = es8311_create(S3_I2C_NUM, ES8311_ADDRESS_0);
	es8311_clock_config_t es_clk = {
		.mclk_inverted = false,
		.sclk_inverted = false,
		.mclk_from_mclk_pin = true,
		.mclk_frequency = 16000 * I2S_MCLK_MULTIPLE_128,
		.sample_frequency = 16000,
	};
	es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16);
	es8311_sample_frequency_config(es_handle, 16000 * I2S_MCLK_MULTIPLE_128, 16000);
	es8311_voice_volume_set(es_handle, 70, NULL);
	es8311_microphone_config(es_handle, false);
	pa_en(1);
	return ESP_OK;
}

i2s_chan_handle_t tx_handle;
esp_err_t i2s_driver_init()
{
	/* 通过辅助宏获取默认的通道配置
	 * 这个辅助宏在 'i2s_common.h' 中定义，由所有 I2S 通信模式共享
	 * 它可以帮助指定 I2S 角色和端口 ID */
	i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);
	chan_cfg.auto_clear = true; // 不清除播放完就和卡机一样
	/* 分配新的 TX 通道并获取该通道的句柄 */
	i2s_new_channel(&chan_cfg, &tx_handle, NULL);

	/* 进行配置，可以通过宏生成声道配置和时钟配置
	 * 这两个辅助宏在 'i2s_std.h' 中定义，只能用于 STD 模式
	 * 它们可以帮助初始化或更新声道和时钟配置 */
	i2s_std_config_t std_cfg = {
		.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000),
		.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
		.gpio_cfg = {
			.mclk = I2S_MCLK_PIN,
			.bclk = I2S_BCLK_PIN,
			.ws = I2S_WS_PIN,
			.dout = I2S_DO_PIN,
			.din = I2S_DI_PIN,
			.invert_flags = {
				.mclk_inv = false,
				.bclk_inv = false,
				.ws_inv = false,
			},
		},
	};
	std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_128;
	/* 初始化通道 */
	ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));
	/* 在写入数据之前，先启用 TX 通道 */
	ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
	/* 如果需要更新声道或时钟配置
	 * 需要在更新前先禁用通道 */
	// i2s_channel_disable(tx_handle);
	// std_cfg.slot_cfg.slot_mode = I2S_SLOT_MODE_MONO; // 默认为立体声
	// i2s_channel_reconfig_std_slot(tx_handle, &std_cfg.slot_cfg);
	// std_cfg.clk_cfg.sample_rate_hz = 96000;
	// i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg);
	return ESP_OK;
}